import React, { Dispatch, MouseEvent, useContext, useMemo, useState } from 'react';
import { TextWidget, getMessage } from '@inbiz/react';
import { useForm } from '@formily/react';
import { request, InbizIcon } from '@inbiz/utils';
import type { UploadProps } from 'antd';
import { Button, Form, Input, message, PaginationProps, Spin, Upload } from 'antd';
import type { RcFile } from 'antd/es/upload';
import Pagination from '@inbiz/basic/Pagination'
import cls from 'classnames';
import { DownloadOutlined } from '@ant-design/icons';
import FullModal from '../../preview/FullModal';
import {
  useCardTmpl,
  useInbizPrefixCls,
  usePagination,
} from '../../hooks';
import tableApi from '../../preview/web/serviceApi';
import { TmplCtx } from './ColumnConfig';
import { CardTmplValueProps, ItemData, PageInfo } from '../../interfaces';
import { builtInTmpl } from '../../shared';

var modules = {
  antd: require('antd'),
  '@ant-design/icons': require('@ant-design/icons'),
  react: require('react'),
};

interface CardTmplSelectProps {
  value?: CardTmplValueProps;
  onChange: (value?: CardTmplValueProps) => void;
}

interface CardTmplProps {
  id: string;
  name: string;
  selectedId: string;
  error: {
    id?: string;
    isError?: boolean;
  };
  tmpl?: any;
  onCardClick: (id: string, name: string, isError?: boolean) => void;
}

interface CardTmplListProps {
  value: CardTmplValueProps;
  error: {
    id?: string;
    isError?: boolean;
  };
  onSelect: Dispatch<CardTmplValueProps>;
}

export type TmplType = {
  id: string;
  name: string;
  node?: React.ReactNode;
  config: {
    [key: string]: ItemData;
  };
  default?: (data: object, emit: (...arg: any) => void) => React.ReactNode;
};

interface TmplUploadProps {
  setLoading: (loading: boolean) => void;
}

const TmplUpload = ({ setLoading }: TmplUploadProps) => {
  const form = useForm();
  const { setTmplInfo } = useContext(TmplCtx);
  const prefixCls = useInbizPrefixCls('card-upload');
  const iconPrefixCls = useInbizPrefixCls('card-upload-icon');
  const titlePrefixCls = useInbizPrefixCls('card-upload-title');
  const uploadProps: UploadProps = {
    maxCount: 1,
    accept: '.jsx',
    fileList: [],
    beforeUpload: (_file: RcFile) => {
      setLoading?.(true);
      tableApi
        .uploadCardTmpl({ file: _file, appId: form.appInfo!.appId })
        .then((res) => {
          request.get(res.path).then((script: string) => {
            if (script) {
              const tmpl = {
                id: res.path,
              } as TmplType;
              try {
                new Function('exports', 'React', 'require', script)(
                  tmpl,
                  React,
                  (moduleName: string) => modules[moduleName],
                );
                tmpl.name = (tmpl.default as any)?.tmplName;
                tmpl.config = (tmpl.default as any).config;
                setTmplInfo((pre: { tmplList: TmplType[] }) => {
                  return {
                    ...pre,
                    tmplList: [tmpl, ...(pre?.tmplList || [])],
                  };
                });
                message.success(getMessage('table.tips.successfullyUpload'));
              } catch (e: any) {
                message.error(`${getMessage('table.tips.templateAnalysisFailed')}${e.message}`);
              }
            }
            setLoading?.(false);
          });
        })
        .catch((err) => {
          setLoading?.(false);
          message.error(getMessage('table.tips.templateUploadFailed'));
        });
      return false;
    },
  };
  return (
    <div className={cls(prefixCls)}>
      <Upload {...uploadProps}>
        <div className={iconPrefixCls}>
          <InbizIcon type="icon-inbiz_basic-upload" style={{ fontSize: 32, color: '#999' }} />
        </div>
        <div className={titlePrefixCls}>
          <TextWidget>table.uploadCustomTemplate</TextWidget>
        </div>
      </Upload>
    </div>
  );
};

const CardTmpl: React.FC<CardTmplProps> = (props) => {
  const { id, name, selectedId, error, tmpl, onCardClick } = props;
  const mainList = Object.keys(tmpl?.config).filter((item) => item !== 'title' && item !== 'img');
  const { setTmplInfo } = useContext(TmplCtx);
  const prefixCls = useInbizPrefixCls('card-designer-content');
  const selectedPrefixCls = useInbizPrefixCls('card-designer-content-selected');
  const errorPrefixCls = useInbizPrefixCls('card-designer-content-error');
  const { tmplNode } = useCardTmpl({ id, name } as TmplType);
  const builtInTmplArr = Object.keys(builtInTmpl);
  const download = (e: MouseEvent) => {
    e.stopPropagation();
    if (builtInTmplArr.includes(id)) {
      tableApi.downloadFile(new Blob([builtInTmpl[id]]), `${name}.jsx`);
      message.success(getMessage('table.tips.downloadSuccessful'));
      return;
    }
    tableApi
      .downloadCardTmpl(id)
      .then(() => message.success(getMessage('table.tips.downloadSuccessful')));
  };

  const deleteTmpl = (e: MouseEvent) => {
    e.stopPropagation();
    setTmplInfo((pre: { tmplList: TmplType[] }) => {
      return {
        ...pre,
        tmplList: pre?.tmplList.filter((tmpl) => tmpl.id !== id),
      };
    });
    message.success(getMessage('table.tips.successfullyDeleted'));
  };

  const handleCardClick = (e: any) => {
    const isError = e.target?.dataset?.tmperror || false;
    onCardClick(id, name, isError);
  };
  const verificationTmp = () => {
    try {
      return tmplNode?.node?.default?.({});
    } catch (error) {
      return (
        <div
          style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', color: 'red' }}
          data-tmperror={true}
        >
          {getMessage('table.tips.templateError')}
        </div>
      );
    }
  };
  return (
    <div
      className={cls([
        prefixCls,
        {
          [selectedPrefixCls]: selectedId === id && error?.id !== id,
          [errorPrefixCls]: selectedId === id && error?.id === id,
        },
      ])}
      onClick={handleCardClick}
    >
      {!builtInTmplArr.includes(tmplNode.id) ? (
        <div style={{ display: 'flex', flex: 1, overflow: 'auto' }}>{verificationTmp()}</div>
      ) : (
        <div className="inbiz-designer-card-item">
          <header>{tmpl.config?.title?.placeText || tmpl.config?.img?.placeText}</header>
          <div className="inbiz-designer-card-item-content">
            {mainList.map((item) => (
              <p key={item}>{tmpl.config[item].placeText}</p>
            ))}
          </div>
        </div>
      )}
      <footer>
        <label>{name}</label>
        <div>
          {!builtInTmplArr.includes(tmplNode.id) && (
            <span onClick={deleteTmpl}>
              <InbizIcon type="icon-shanchu-h" style={{ marginRight: 12, cursor: 'pointer' }} />
            </span>
          )}
          <DownloadOutlined key={'download'} onClick={download} />
        </div>
      </footer>
    </div>
  );
};

const CardTmplList = ({ value, onSelect, error }: CardTmplListProps) => {
  const prefixCls = useInbizPrefixCls('card-designer');
  const bodyPrefixCls = useInbizPrefixCls('card-designer-body');

  const {
    tmplInfo: { tmplList = [] },
  } = useContext(TmplCtx);

  const [loading, setLoading] = useState(false);
  const [selectedId, setSelectedId] = useState<string>(value.id);
  const [pageInfo, setPageInfo] = useState({ current: 1, pageSize: 10 });
  const pagination: PaginationProps = usePagination({
    ...pageInfo,
    total: tmplList.length,
  });
  const onPageChange = (pageParam: PageInfo) => {
    setPageInfo(pageParam);
  };

  const onCardClick = (id: string, name: string, isError?: boolean) => {
    setSelectedId(id);
    onSelect({ name, id, isError });
  };

  return (
    <Spin spinning={loading}>
      <div className={cls(prefixCls)}>
        <div className={cls(bodyPrefixCls)}>
          <TmplUpload setLoading={setLoading} />
          {tmplList
            .slice((pageInfo.current - 1) * pageInfo.pageSize, pageInfo.current * pageInfo.pageSize)
            .map((tmpl) => {
              return (
                <CardTmpl
                  key={tmpl.id}
                  id={tmpl.id}
                  name={tmpl.name}
                  tmpl={tmpl}
                  error={error}
                  selectedId={selectedId || value.id}
                  onCardClick={onCardClick}
                />
              );
            })}
        </div>
        {pagination?.total && pagination?.total > 10 && (
          <Pagination pagination={pagination} onPageChange={onPageChange} />
        )}
      </div>
    </Spin>
  );
};

const CardTmplSelect = (props: CardTmplSelectProps) => {
  const {
    value = {
      name: '默认模板',
      id: 'default',
    },
    onChange,
  } = props;
  const { onSaveCardTmpl } = useContext(TmplCtx);
  const [visible, setVisible] = useState(false);
  const [error, setError] = useState<{ id?: string; isError?: boolean }>({});
  const [selectedTmpl, setSelectedTmpl] = useState<CardTmplValueProps>(value);

  const openModal = () => {
    setVisible(true);
  };

  const closeModal = () => {
    if (!selectedTmpl) {
      message.error(getMessage('table.tips.pleaseSelectTheTemplate'));
      return;
    }
    setVisible(false);
  };

  const onOk = () => {
    if (selectedTmpl?.isError) {
      setError({
        id: selectedTmpl.id,
        isError: selectedTmpl?.isError,
      });
      return;
    }
    onChange(selectedTmpl);
    onSaveCardTmpl?.(selectedTmpl.id);
    closeModal();
  };

  const renderCardTmpList = useMemo(() => {
    return <CardTmplList value={value} error={error} onSelect={setSelectedTmpl} />;
  }, [selectedTmpl, error, value]);

  return (
    <>
      <Form.Item style={{ display: 'inline-flex', width: 'calc(50% - 12px)', marginRight: 20 }}>
        <Input readOnly value={value?.name} />
      </Form.Item>
      <Button type="primary" onClick={openModal}>
        选择
      </Button>
      <FullModal
        className="inbiz-card-tmpl-modal"
        title={getMessage('table.choiceTemplate')}
        width={'60%'}
        style={{ top: 0 }}
        visible={visible}
        onOk={onOk}
        centered
        destroyOnClose
        onCancel={closeModal}
        render={renderCardTmpList}
      />
    </>
  );
};

export default CardTmplSelect;
